#pragma once
#include <iostream>
#include <vector>
#include <pthread.h>
#include "Sem.hpp"
#include "Mutex.hpp"

namespace RingBufferModule
{
	using namespace SemModule;
	using namespace LockModule;

	template <typename T>
	class RingBuffer
	{

	public:
		RingBuffer(int cap)
			: _ring(cap), _cap(cap), _p_step(0), _c_step(0), _spacesem(cap),_datasem(0)
		{
		}
		void Equeue(const T &in)
		{
			// 生产者

			//对信号量的值进行减 1 操作。如果信号量的值大于 0，执行 P 操作后，线程或进程可以继续执行；
			//如果信号量的值等于 0，执行 P 操作的线程或进程会被阻塞，直到信号量的值大于 0

			_spacesem.P(); // 申请资源，尝试获取空间信号量，若空间不足（信号量为0 ）则阻塞

			// pthread_mutex_lock(&_p_lock);  // 先申请信号量再加锁

			// 临界区:操作共享的环形缓冲区，需要加锁保护
			{
				LockGuard lockguard(_p_lock);
				_ring[_p_step] = in; // 生产
				_p_step++;
				_p_step %= _cap; // 维持环形特性
			}

			// pthread_mutex_unlock(&_p_lock);

			// 对信号量的值进行加 1 操作。如果有线程或进程因执行 P 操作而阻塞，V 操作会唤醒其中一个阻塞的线程或进程。
			_datasem.V(); //发布数据信号量，通知消费者有新数据可消费
		}
		void Pop(T *out)
		{
			// 消费者

			_datasem.P();
			// pthread_mutex_lock(&_c_lock);

			{
				LockGuard lockguard(_c_lock);
				*out = _ring[_c_step];
				_c_step++;
				_c_step %= _cap;
			}

			// pthread_mutex_unlock(&_c_lock);
			_spacesem.V();
		}
		~RingBuffer()
		{
		}

	private:
		std::vector<T> _ring; // 环 -- 临界资源
		int _cap;			  // 总容量
		int _p_step;		  // 生产者位置
		int _c_step;		  // 消费者位置

		Sem _datasem;  // 数据信号量
		Sem _spacesem; // 空间信号量

		Mutex _p_lock;
		Mutex _c_lock;
	};
};